import { error } from '../../../output.js'
import { getVariable } from '../../../tools/variable.js'

const run = (runtime, ast, node) => {
  if (node.type !== 'AssignmentExpression') return

  const value = getVariable(runtime, node.right.name, ast.filepath)

  const _run = (runtime) => {
    const scope = runtime.scopes.get(ast.filepath)
    const variable = scope.get('variable')
    const constant = scope.get('constant')

    if (node.left.type === 'Identifier') {
      if (variable.has(node.left.name)) {
        if (node.right.type === 'Literal' || node.right.type === 'Identifier') {
          switch (node.operator) {
            case '=':
              if (node.right.type === 'Literal') {
                if (typeof variable.get(node.left.name) !== typeof node.right.value) {
                  const value = typeof node.right.value === 'string' ? `"${ node.right.value }"` : node.right.value
                  error(`TypeError: ${ value } is not a ${ typeof variable.get(node.left.name) }`)
                }
                variable.set(node.left.name, node.right.value)
              } else {
                if (value !== null) {
                  if (typeof variable.get(node.left.name) !== typeof value) {
                    error(`TypeError: ${ node.right.name } is not a ${ typeof variable.get(node.left.name) }`)
                  }
                  variable.set(node.left.name, value)
                } else {
                  error(`Variable not found: ${ node.right.name }`)
                }
              }
              break
            case '+=':
            case '-=':
            case '*=':
            case '/=':
            case '%=':
              if (node.right.type === 'Literal') {
                if (typeof variable.get(node.left.name) !== 'number') {
                  error(`TypeError: ${ node.left.name } is not a number`)
                }
                if (typeof node.right.value !== 'number') {
                  error(`TypeError: ${ node.right.value } is not a number`)
                }
                switch (node.operator) {
                  case '+=':
                    variable.set(node.left.name, variable.get(node.left.name) + node.right.value)
                    break
                  case '-=':
                    variable.set(node.left.name, variable.get(node.left.name) - node.right.value)
                    break
                  case '*=':
                    variable.set(node.left.name, variable.get(node.left.name) * node.right.value)
                    break
                  case '/=':
                    variable.set(node.left.name, variable.get(node.left.name) / node.right.value)
                    break
                  case '%=':
                    variable.set(node.left.name, variable.get(node.left.name) % node.right.value)
                    break
                }
              } else {
                if (value !== null) {
                  if (typeof variable.get(node.left.name) !== 'number') {
                    error(`TypeError: ${ node.left.name } is not a number`)
                  }
                  if (typeof value !== 'number') {
                    error(`TypeError: ${ node.right.name } is not a number`)
                  }
                  switch (node.operator) {
                    case '+=':
                      variable.set(node.left.name, variable.get(node.left.name) + value)
                      break
                    case '-=':
                      variable.set(node.left.name, variable.get(node.left.name) - value)
                      break
                    case '*=':
                      variable.set(node.left.name, variable.get(node.left.name) * value)
                      break
                    case '/=':
                      variable.set(node.left.name, variable.get(node.left.name) / value)
                      break
                    case '%=':
                      variable.set(node.left.name, variable.get(node.left.name) % value)
                      break
                  }
                } else {
                  error(`Variable not found: ${ node.right.name }`)
                }
              }
              break
            default:
              error(`Can't handle operator: ${ node.operator }`)
          }
        } else {
          error(`Can't handle type: ${ node.right.type }`)
        }
      } else if (constant.has(node.left.name)) {
        error(`Can't assign to constant: ${ node.left.name }`)
      } else {
        if (runtime.parent !== null) return _run(runtime.parent)
        error(`Variable not found: ${ node.left.name }`)
      }
    } else {
      error(`Can't handle type: ${ node.left.type }`)
    }
  }

  _run(runtime)
}

export default run